{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 搭一个回测框架\n",
    "> 针对永续合约，手续费万2，滑点万2或者0.1usdt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 加载包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import os\n",
    "import time\n",
    "from numba import jit\n",
    "import talib\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 回测参数设置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: 'E:\\\\0408season_factors\\\\pair_list_dir\\\\main\\\\test_pair_list_btc_usdt_main.npy'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
      "Input \u001b[1;32mIn [9]\u001b[0m, in \u001b[0;36m<cell line: 5>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[38;5;66;03m# 回测时间\u001b[39;00m\n\u001b[0;32m      4\u001b[0m test_pair_list_dir \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mr\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mE:\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124m0408season_factors\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124mpair_list_dir\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124mmain\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;66;03m# Moss\u001b[39;00m\n\u001b[1;32m----> 5\u001b[0m test_pair_list \u001b[38;5;241m=\u001b[39m \u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mload\u001b[49m\u001b[43m(\u001b[49m\u001b[43mos\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpath\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtest_pair_list_dir\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mtest_pair_list_\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43mcode\u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m_main.npy\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43mallow_pickle\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[0;32m      6\u001b[0m \u001b[38;5;66;03m# 因子参数\u001b[39;00m\n\u001b[0;32m      7\u001b[0m factor_list \u001b[38;5;241m=\u001b[39m [[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtrend_bp_bolling_v1[300, 3000, 1500, 120]\u001b[39m\u001b[38;5;124m'\u001b[39m,\u001b[38;5;241m1500\u001b[39m],]\n",
      "File \u001b[1;32md:\\Anaconda\\lib\\site-packages\\numpy\\lib\\npyio.py:417\u001b[0m, in \u001b[0;36mload\u001b[1;34m(file, mmap_mode, allow_pickle, fix_imports, encoding)\u001b[0m\n\u001b[0;32m    415\u001b[0m     own_fid \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[0;32m    416\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 417\u001b[0m     fid \u001b[38;5;241m=\u001b[39m stack\u001b[38;5;241m.\u001b[39menter_context(\u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mos_fspath\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrb\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m)\n\u001b[0;32m    418\u001b[0m     own_fid \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m    420\u001b[0m \u001b[38;5;66;03m# Code to distinguish from NumPy binary files and pickles.\u001b[39;00m\n",
      "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'E:\\\\0408season_factors\\\\pair_list_dir\\\\main\\\\test_pair_list_btc_usdt_main.npy'"
     ]
    }
   ],
   "source": [
    "# 回测品种\n",
    "code = 'btc_usdt'\n",
    "# 回测时间\n",
    "test_pair_list_dir = r'E:\\0408season_factors\\pair_list_dir\\main' # Moss\n",
    "test_pair_list = np.load(os.path.join(test_pair_list_dir, 'test_pair_list_'+code+'_main.npy'),allow_pickle=True)\n",
    "# 因子参数\n",
    "factor_list = [['trend_bp_bolling_v1[300, 3000, 1500, 120]',1500],]\n",
    "# 回测参数\n",
    "back_config={\n",
    "    'jump_point':5e-4, #滑点对应几跳\n",
    "    'fee_point':2e-4, #n倍手续费\n",
    "    'mode':2,     #1按手数回测 2按资金回测\n",
    "    'start_money':1000000,\n",
    "    'lev':1,\n",
    "    'rev':1, #-1反转信号\n",
    "    'by_tick':True,#True按照滑点跳数折算手续费\n",
    "    'pre_process':'pre_process_func',\n",
    "}\n",
    "# 策略频率\n",
    "test_freq = '30min'\n",
    "# 回测结果保存位置\n",
    "save_path = os.path.join(r'E:\\0408season_factors\\trend_bp_bolling\\v2','{}_{}'.format(code, test_freq)) # Moss\n",
    "# 结果中是否保留信号\n",
    "save_signal=True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 因子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_dict_str = \"{'data': data_in}\"\n",
    "    \n",
    "def sig_cal(data,factor,data_dict):\n",
    "\n",
    "    temp = factor.split('[',1)\n",
    "    fun = eval(temp[0])\n",
    "    parm = eval('['+temp[1])\n",
    "    return fun(data,parm)\n",
    "\n",
    "@jit(nopython=True)\n",
    "def super_passband_filter(price,window_list):\n",
    "    a1 = 0.0\n",
    "    a2 = 0.0\n",
    "    PB = np.zeros_like(price)\n",
    "    RMS = np.zeros_like(price)\n",
    "    \n",
    "    for i in range(len(price)):\n",
    "        if i > 1:\n",
    "            a1 = 5 / window_list[0]\n",
    "            a2 = 5 / window_list[1]\n",
    "            PB[i] = (a1 - a2) * price[i] + (a2 * (1 - a1) - a1 * (1 - a2)) * price[i-1] + (1 - a1 + 1 - a2) * PB[i-1] - (1 - a1) * (1 - a2) * PB[i-2]\n",
    "            if i > window_list[2]:\n",
    "                RMS[i] = np.sqrt(np.sum(PB[i-window_list[2]:i] * PB[i-window_list[2]:i]) / (window_list[0]-1))\n",
    "\n",
    "    return PB, RMS\n",
    "\n",
    "def rsi_np(price, n=14):\n",
    "    # 计算价格的涨跌幅\n",
    "    delta = np.diff(price)\n",
    "    # 分别计算涨幅和跌幅的平均值\n",
    "    gain = delta.copy()\n",
    "    loss = delta.copy()\n",
    "    gain[gain < 0] = 0\n",
    "    loss[loss > 0] = 0\n",
    "    avg_gain = talib.SMA(gain, n)\n",
    "    avg_loss = talib.SMA(-loss, n)\n",
    "\n",
    "    # 计算RSI指标\n",
    "    rs = avg_gain / avg_loss\n",
    "    rsi = 100 - (100 / (1 + rs))\n",
    "    rsi = np.insert(rsi, 0, np.nan)\n",
    "    return rsi\n",
    "\n",
    "@jit(nopython=True)\n",
    "def gen_sig(signal, rsi, pb, rms, window_list):\n",
    "    for i in range(window_list[2]+window_list[3],len(signal)):\n",
    "        signal[i]=signal[i-1]\n",
    "        if rsi[i-1]>=window_list[4] and ((pb[i-2]>rms[i-2] and pb[i-1]<=rms[i-1]) or (pb[i-2]>0 and pb[i-1]<=0) or (pb[i-2]>-rms[i-2] and pb[i-1]<=-rms[i-1])): # 下穿上轨做空\n",
    "            signal[i]=-1# 下穿上轨做空 # 下穿中轨做空 # 下穿下轨做空\n",
    "        elif rsi[i-1]<=window_list[5] and ((pb[i-2]<=-rms[i-2] and pb[i-1]>-rms[i-1]) or (pb[i-2]<=0 and pb[i-1]>0) or (pb[i-2]<=rms[i-2] and pb[i-1]>rms[i-1])): # 上穿下轨做空\n",
    "            signal[i]=1\n",
    "    return signal\n",
    "\n",
    "def core_for_trend_bp_bolling(price,window_list):\n",
    "    signal = np.zeros(len(price))\n",
    "    pb, rms = super_passband_filter(price,window_list)\n",
    "    rsi = rsi_np(price, n=window_list[3])\n",
    "    signal = gen_sig(signal, rsi, pb, rms, window_list)\n",
    "    return signal, pb, rms\n",
    "\n",
    "def trend_bp_bolling_v2(market_data_in,window_list):\n",
    "    market_data_in = market_data_in.copy()\n",
    "    price = market_data_in['close'].values\n",
    "    signal, pb, rms = core_for_trend_bp_bolling(price,np.array(window_list).astype(float))\n",
    "\n",
    "    market_data_in['signal'] = signal\n",
    "    market_data_in['pb'] = pb\n",
    "    market_data_in['rms'] = rms\n",
    "    return market_data_in"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_feather(r\"E:\\Quant\\USDT_Kline\\30min\\BTCUSDT.feather\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Open Time</th>\n",
       "      <th>Open</th>\n",
       "      <th>High</th>\n",
       "      <th>Low</th>\n",
       "      <th>Close</th>\n",
       "      <th>Volume</th>\n",
       "      <th>Close Time</th>\n",
       "      <th>Quote Asset Volume</th>\n",
       "      <th>Number of Trades</th>\n",
       "      <th>TB Base Volume</th>\n",
       "      <th>TB Quote Volume</th>\n",
       "      <th>Ignore</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>2017-08-17 04:00:00</td>\n",
       "      <td>4261.48</td>\n",
       "      <td>4280.56</td>\n",
       "      <td>4261.32</td>\n",
       "      <td>4261.45</td>\n",
       "      <td>11.308926</td>\n",
       "      <td>2017-08-17 04:29:59.999000064</td>\n",
       "      <td>4.822475e+04</td>\n",
       "      <td>49</td>\n",
       "      <td>3.936174</td>\n",
       "      <td>1.679304e+04</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2017-08-17 04:30:00</td>\n",
       "      <td>4280.00</td>\n",
       "      <td>4313.62</td>\n",
       "      <td>4267.99</td>\n",
       "      <td>4308.83</td>\n",
       "      <td>35.872083</td>\n",
       "      <td>2017-08-17 04:59:59.999000064</td>\n",
       "      <td>1.541414e+05</td>\n",
       "      <td>122</td>\n",
       "      <td>31.224329</td>\n",
       "      <td>1.341594e+05</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>2017-08-17 05:00:00</td>\n",
       "      <td>4308.83</td>\n",
       "      <td>4328.69</td>\n",
       "      <td>4304.31</td>\n",
       "      <td>4320.00</td>\n",
       "      <td>21.048648</td>\n",
       "      <td>2017-08-17 05:29:59.999000064</td>\n",
       "      <td>9.086429e+04</td>\n",
       "      <td>73</td>\n",
       "      <td>19.396570</td>\n",
       "      <td>8.374608e+04</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>2017-08-17 05:30:00</td>\n",
       "      <td>4320.00</td>\n",
       "      <td>4320.00</td>\n",
       "      <td>4291.37</td>\n",
       "      <td>4315.32</td>\n",
       "      <td>2.186268</td>\n",
       "      <td>2017-08-17 05:59:59.999000064</td>\n",
       "      <td>9.440531e+03</td>\n",
       "      <td>29</td>\n",
       "      <td>2.051501</td>\n",
       "      <td>8.862196e+03</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2017-08-17 06:00:00</td>\n",
       "      <td>4330.29</td>\n",
       "      <td>4330.29</td>\n",
       "      <td>4309.37</td>\n",
       "      <td>4311.02</td>\n",
       "      <td>3.566277</td>\n",
       "      <td>2017-08-17 06:29:59.999000064</td>\n",
       "      <td>1.540976e+04</td>\n",
       "      <td>14</td>\n",
       "      <td>2.302077</td>\n",
       "      <td>9.951673e+03</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>118321</th>\n",
       "      <td>2024-05-22 22:00:00</td>\n",
       "      <td>69497.77</td>\n",
       "      <td>69550.00</td>\n",
       "      <td>69241.36</td>\n",
       "      <td>69335.99</td>\n",
       "      <td>423.070270</td>\n",
       "      <td>2024-05-22 22:29:59.999000064</td>\n",
       "      <td>2.934311e+07</td>\n",
       "      <td>37351</td>\n",
       "      <td>194.666760</td>\n",
       "      <td>1.350263e+07</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>118322</th>\n",
       "      <td>2024-05-22 22:30:00</td>\n",
       "      <td>69335.98</td>\n",
       "      <td>69335.99</td>\n",
       "      <td>69054.01</td>\n",
       "      <td>69141.28</td>\n",
       "      <td>530.408920</td>\n",
       "      <td>2024-05-22 22:59:59.999000064</td>\n",
       "      <td>3.667791e+07</td>\n",
       "      <td>34730</td>\n",
       "      <td>241.146420</td>\n",
       "      <td>1.667348e+07</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>118323</th>\n",
       "      <td>2024-05-22 23:00:00</td>\n",
       "      <td>69141.28</td>\n",
       "      <td>69233.69</td>\n",
       "      <td>69023.56</td>\n",
       "      <td>69103.94</td>\n",
       "      <td>514.773410</td>\n",
       "      <td>2024-05-22 23:29:59.999000064</td>\n",
       "      <td>3.558390e+07</td>\n",
       "      <td>45044</td>\n",
       "      <td>236.094350</td>\n",
       "      <td>1.631994e+07</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>118324</th>\n",
       "      <td>2024-05-22 23:30:00</td>\n",
       "      <td>69103.94</td>\n",
       "      <td>69227.56</td>\n",
       "      <td>69071.54</td>\n",
       "      <td>69166.62</td>\n",
       "      <td>203.847740</td>\n",
       "      <td>2024-05-22 23:59:59.999000064</td>\n",
       "      <td>1.409422e+07</td>\n",
       "      <td>11987</td>\n",
       "      <td>110.745120</td>\n",
       "      <td>7.656763e+06</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>118325</th>\n",
       "      <td>2024-05-23 00:00:00</td>\n",
       "      <td>69166.62</td>\n",
       "      <td>69434.17</td>\n",
       "      <td>69166.61</td>\n",
       "      <td>69355.30</td>\n",
       "      <td>346.057570</td>\n",
       "      <td>2024-05-23 00:29:59.999000064</td>\n",
       "      <td>2.398812e+07</td>\n",
       "      <td>17795</td>\n",
       "      <td>204.230670</td>\n",
       "      <td>1.415562e+07</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>118326 rows × 12 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "                 Open Time      Open      High       Low     Close  \\\n",
       "0      2017-08-17 04:00:00   4261.48   4280.56   4261.32   4261.45   \n",
       "1      2017-08-17 04:30:00   4280.00   4313.62   4267.99   4308.83   \n",
       "2      2017-08-17 05:00:00   4308.83   4328.69   4304.31   4320.00   \n",
       "3      2017-08-17 05:30:00   4320.00   4320.00   4291.37   4315.32   \n",
       "4      2017-08-17 06:00:00   4330.29   4330.29   4309.37   4311.02   \n",
       "...                    ...       ...       ...       ...       ...   \n",
       "118321 2024-05-22 22:00:00  69497.77  69550.00  69241.36  69335.99   \n",
       "118322 2024-05-22 22:30:00  69335.98  69335.99  69054.01  69141.28   \n",
       "118323 2024-05-22 23:00:00  69141.28  69233.69  69023.56  69103.94   \n",
       "118324 2024-05-22 23:30:00  69103.94  69227.56  69071.54  69166.62   \n",
       "118325 2024-05-23 00:00:00  69166.62  69434.17  69166.61  69355.30   \n",
       "\n",
       "            Volume                    Close Time  Quote Asset Volume  \\\n",
       "0        11.308926 2017-08-17 04:29:59.999000064        4.822475e+04   \n",
       "1        35.872083 2017-08-17 04:59:59.999000064        1.541414e+05   \n",
       "2        21.048648 2017-08-17 05:29:59.999000064        9.086429e+04   \n",
       "3         2.186268 2017-08-17 05:59:59.999000064        9.440531e+03   \n",
       "4         3.566277 2017-08-17 06:29:59.999000064        1.540976e+04   \n",
       "...            ...                           ...                 ...   \n",
       "118321  423.070270 2024-05-22 22:29:59.999000064        2.934311e+07   \n",
       "118322  530.408920 2024-05-22 22:59:59.999000064        3.667791e+07   \n",
       "118323  514.773410 2024-05-22 23:29:59.999000064        3.558390e+07   \n",
       "118324  203.847740 2024-05-22 23:59:59.999000064        1.409422e+07   \n",
       "118325  346.057570 2024-05-23 00:29:59.999000064        2.398812e+07   \n",
       "\n",
       "        Number of Trades  TB Base Volume  TB Quote Volume Ignore  \n",
       "0                     49        3.936174     1.679304e+04      0  \n",
       "1                    122       31.224329     1.341594e+05      0  \n",
       "2                     73       19.396570     8.374608e+04      0  \n",
       "3                     29        2.051501     8.862196e+03      0  \n",
       "4                     14        2.302077     9.951673e+03      0  \n",
       "...                  ...             ...              ...    ...  \n",
       "118321             37351      194.666760     1.350263e+07      0  \n",
       "118322             34730      241.146420     1.667348e+07      0  \n",
       "118323             45044      236.094350     1.631994e+07      0  \n",
       "118324             11987      110.745120     7.656763e+06      0  \n",
       "118325             17795      204.230670     1.415562e+07      0  \n",
       "\n",
       "[118326 rows x 12 columns]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x12699468f10>]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(df['Close'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 回测函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pre_process_func(org_pair_data,time_list):#预处理函数，可重写，加载回看期数据\n",
    "    backtrace_time=time_list[-1]\n",
    "    ret = org_pair_data\n",
    "    if org_pair_data.trade_date[0]>time_list[0]:\n",
    "        return ret[ret.trade_date<time_list[1]]\n",
    "    else:\n",
    "        temp_len = len(org_pair_data[org_pair_data.trade_date<time_list[0]])\n",
    "        if temp_len>backtrace_time: \n",
    "            \n",
    "            ret = org_pair_data.iloc[temp_len-backtrace_time:,:].reset_index(drop=True)\n",
    "            return ret[ret.trade_date<time_list[1]]\n",
    "    return  ret[ret.trade_date<time_list[1]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_data(symbol_name, data_dir):\n",
    "    return pd.read_feather('%s/%s.feather'%(data_dir, symbol_name), use_threads=False).reset_index(drop=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@jit(nopython=True)\n",
    "def back_test_core_v1(signal, price, value_nums, fee, jump):\n",
    "    gap = np.zeros(len(signal))\n",
    "    for i in range(1,len(signal)):\n",
    "        f = np.abs(signal[i]-signal[i-1])*value_nums*price[i-1]*fee #手续费 fee/手续费百分比  交易金额: 开仓手数*每手吨数*每吨价值  *手续费百分比\n",
    "        #滑点    # 开多仓情况 买的价格多一个Jump，开空仓情况  卖的价格少一个Jump，收益都会少一个jump\n",
    "        f += jump*np.abs(signal[i]-signal[i-1])*value_nums  # signal 代表的是手数/仓位  手数*每手吨数* 滑点\n",
    "        gap[i] = (price[i]-price[i-1])*signal[i]*value_nums  \n",
    "        gap[i]-=f\n",
    "    return gap\n",
    "\n",
    "@jit(nopython=True)\n",
    "def sig_pos_trans(signal,price,each,value_nums):#仓位转换\n",
    "    pos = np.zeros(len(signal))\n",
    "    for i in range(1,len(signal)):\n",
    "        pos[i] = float(signal[i]*each//(value_nums*price[i-1]))\n",
    "    return pos\n",
    "    \n",
    "def back_test_v1(price,signal,value_nums,fee = 0.0004,jump=1,start_money=1000000,lev=0.1):\n",
    "    each = start_money*lev\n",
    "    pos = sig_pos_trans(signal,price,each,value_nums)\n",
    "    gap = back_test_core_v1(pos,price,value_nums,fee,jump)        \n",
    "    return pd.Series(gap)\n",
    "\n",
    "def back_test_v2(price,signal,value_nums,fee = 0.0004,jump=1):\n",
    "    gap = back_test_core_v1(signal,price,value_nums,fee,jump)        \n",
    "    return pd.Series(gap)\n",
    "\n",
    "def gain_cal_precision(data_in,value_nums=20,mode=1,fee = 0.0004,jump=1,start_money=1000000,lev=0.1,rev=1):\n",
    "    time_line = data_in[['trade_date']].copy().reset_index(drop=True)\n",
    "    \n",
    "    price =  data_in['close'].values\n",
    "    sig = data_in['signal'].values\n",
    "    sig[:5]=0\n",
    "    sig[-5:]=0\n",
    "    if mode != 1:\n",
    "        time_line['gain'] = back_test_v1(price,sig*rev,value_nums=value_nums,fee=fee,jump=jump,\n",
    "                                         start_money=start_money,lev=lev)\n",
    "    else:\n",
    "        time_line['gain'] = back_test_v2(price,sig*rev,value_nums=value_nums,fee=fee,jump=jump)\n",
    "        \n",
    "    gain_last = time_line.groupby(time_line.trade_date.astype('str').str[:])['gain'].sum().cumsum()\n",
    "    \n",
    "    return gain_last"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def back_test(factor,symbol,back_config,data_dict_str,load_path,time_list=[]):\n",
    "    '''\n",
    "    永续合约择时回测函数\n",
    "    Parmeters:\n",
    "        factor: List, such as ['trend_bp_bolling_v1[300, 3000, 1500, 120]',1500]\n",
    "        symbol: String, contract name such as 'BTC_USDT'\n",
    "        back_config: Dict\n",
    "        data_dict_str: String, such as \"{'data': data_in}\"\n",
    "        load_path: String, market data dir\n",
    "        time_list: List, [start time, end time]\n",
    "    Return:\n",
    "        gain: pd.Dataframe\n",
    "    '''\n",
    "    back_dict=dict(\n",
    "    jump =  back_config['jump_point'],\n",
    "    fee = back_config['fee_point'],\n",
    "    value_nums = 1,\n",
    "    mode = back_config['mode'],\n",
    "    start_money = back_config['start_money'],\n",
    "    lev = back_config['lev'],\n",
    "    by_tick = back_config['by_tick']\n",
    "    )\n",
    "    if 'rev' in back_config:back_dict['rev']=back_config['rev']\n",
    "    try:\n",
    "        data_in = load_data(symbol, load_path)\n",
    "    except Exception as e:\n",
    "        # print(e)\n",
    "        return [0]\n",
    "\n",
    "    if 'pre_process' in back_config and back_config['pre_process'] is not None:\n",
    "        data_in = eval(back_config['pre_process'])(data_in,time_list)\n",
    "    try:\n",
    "        data_dict = eval(data_dict_str)\n",
    "        data_in = sig_cal(data_in,factor,data_dict)\n",
    "        data_in['signal'] = data_in['signal'].fillna(method='ffill').fillna(0)\n",
    "        if len(time_list)!=0:\n",
    "            data_in = data_in[(data_in.trade_date>time_list[0])&(data_in.trade_date<time_list[1])].reset_index(drop=True)\n",
    "        gain = gain_cal_precision(data_in,**back_dict)\n",
    "    except Exception as e:\n",
    "        print(factor,symbol)\n",
    "        print(e)  \n",
    "        return [0]\n",
    "    return gain"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "undefined.undefined.undefined"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
